import { BaseComp } from '../../../common/component/comp';
import { ZRenderComp } from '../../../common/component/zrComp';

import { MoonRecord } from '../../compute/moonRecords';

import layout from './layouts';

/**
 * MoonRecordView
 * ├── Background (z-index=-1) 含 Title
 * │   ├── TableHeader (z-index=0)
 * ├── Row (z-index=0)
 * │   ├── CountryTag (z-index=0)
 * ├── SummaryRow (z-index=0)
 * │   ├── CountryTag (z-index=0)
 */

class MoonRecordView extends BaseComp {
	constructor(args) {
		super(null);
    this.context = args.context;
    /** @type {HTMLElement} */ this.dom = args.dom;
    this.zrender = args.zrender;
    this.zr = this.zrender.init(this.dom);
    this.defer(() => this.zr.dispose());

    this.bg = new Background({ parent : this, zr : this.zr, zrender : this.zrender });
    /** @type {Row[]} */
    this.rows = [];
    /** @type {SummaryRow[]} */
    this.summaryRows = [];
  }

  /**
   * @param {MoonRecord[]} records
   */
  setRecords(records, showFirstSummary = true) {
    this.rows.forEach(row => row.destroy());
    this.rows = records.map(record => new Row({
      parent : this,
      zr : this.zr,
      zrender : this.zrender,
      record,
    }));
    this.summaryRows.forEach(row => row.destroy());

    if (this.rows.length == 0) {
      return;
    }

    // 为每一个 rows 设置位置
    // 如果前后的 year 除了最后一位以外不同的话, 中间塞一个标签行
    let lastY = layout.ROW_FIRST_Y;
    let lastYearS = 0;
    /** @type {{y:number, year:number, record:object}[]}
     * record 是 key:country, value:[总次数, 成功次数] */
    const summaryDescs = [];
    /** @type {{y:number, year:number, record:object}} */
    let currentDesc = null;

    for (let i = 0; i < this.rows.length; i++) {
      const record = records[i];

      let row = this.rows[i];
      let currentYearS = parseInt(record.year / 10);
      if (currentYearS != lastYearS) {
        lastYearS = currentYearS;
        currentDesc = {
          y: lastY,
          year: lastYearS * 10,
          record: {}
        };
        summaryDescs.push(currentDesc);

        lastY += layout.SUMMARY_HEIGHT;
      }

      row.x = layout.ROW_X;
      row.y = lastY;
      lastY += row.height;

      const success = record.isSuccess() ? 1 : 0;
      record.countries.forEach(c => {
        if (currentDesc.record[c] == null) {
          currentDesc.record[c] = [1, success];
          return;
        }

        let record = currentDesc.record[c];
        record[0] += 1;
        if (success) {
          record[1] += 1;
        }
      });
    }

    this.summaryRows = summaryDescs.map((desc, index) => {
      let srow = new SummaryRow({
        parent : this,
        zr : this.zr,
        zrender : this.zrender,
        year: desc.year,
        record: desc.record,
        showRecord: (showFirstSummary || index != 0),
      });

      srow.x = layout.ROW_X;
      srow.y = desc.y;

      return srow;
    });

    this.resizeForAllLayers(layout.PANEL_WIDTH, lastY + layout.FOOTER_HEIGHT);
  }

  resizeForAllLayers(width, height) {
    this.dom.style.width = width + 'px';
    this.dom.style.height = height + 'px';

    this.zr.resize({ width, height });
    let findCanvasAndResize = function(el) {
      if (el instanceof HTMLCanvasElement) {
        el.width = width;
        el.height = height;
        el.style.width = width + 'px';
        el.style.height = height + 'px';
      } else if (el instanceof HTMLDivElement) {
        let children = el.children;
        for (let i = 0; i < children.length; i++) {
          findCanvasAndResize(children[i]);
        }
      }
    }

    // 这可能是 bug, 需要手动将 canvas 重置大小, 而且重置之后 refresh 都不能重绘, 必须改变一下它元素的属性
    findCanvasAndResize(this.dom);
    this.emit('view/resize', { width, height });
  }

  setBgColor(color) {
    this.bg.setBgColor(color);
  }
}

const columnDesc = [
  {
    x : layout.iconXs.overflight,
    names : ['飞越'],
    icon : layout.paths.overflight,
  },
  {
    x : layout.iconXs.surround,
    names : ['环绕'],
    icon : layout.paths.surround,
  },
  {
    x : layout.iconXs.return_orbit,
    names : ['环绕后','返回'],
    icon : layout.paths.return_orbit,
  },
  {
    x : layout.iconXs.landing_hard,
    names : ['硬着陆'],
    icon : layout.paths.landing_hard,
  },
  {
    x : layout.iconXs.landing_soft,
    names : ['软着陆'],
    icon : layout.paths.landing_soft,
  },
  {
    x : layout.iconXs.landing_rover,
    names : ['巡视'],
    icon : layout.paths.landing_rover,
  },
  {
    x : layout.iconXs.return_land,
    names : ['着陆后','返回'],
    icon : layout.paths.return_land,
  }
];

class Background extends ZRenderComp {
  constructor(args) {
		super(args.parent, args.zrender);
    this.useExistZRenderInstance(args.zr, 0, 0);

    this.on('view/resize', (_, value) => {
      this.onResize(value);
    });

    this.header = new TableHeader({
      zrender: this.zrender,
      zr: this.zr,
      parent: this,
      x: layout.ROW_X,
      y: layout.TABLE_HEADER_Y,
      iconStartY: layout.TABLE_HEADER_ICON_Y,
      textStartY: layout.TABLE_HEADER_TEXT_Y,
    });

    this.footer = new TableHeader({
      zrender: this.zrender,
      zr: this.zr,
      parent: this,
      x: layout.ROW_X,
      y: -200,
      iconStartY: layout.TABLE_FOOTER_ICON_Y,
      textStartY: layout.TABLE_FOOTER_TEXT_Y,
    });

    let spTx1 = this.createText('人类探月形势', 100)
      .textFill('#FFF')
      .position(80, 180)
      .textStyle('left', 'bottom', 'bold', '华文中宋')
      .build();
    this.createText('汇总', 70)
      .textFill('#FFF')
      .position(90 + spTx1.getBoundingRect().width, 180)
      .textStyle('left', 'bottom', 'bold', '华文中宋')
      .opacity(0.75)
      .build();
    this.createText('创意来自星球研究所 | 重置 Zdream | 截至北京时间 2022 年 12 月 14 日', 24)
      .textFill('#FFF')
      .position(layout.PANEL_WIDTH - 120, 210)
      .textStyle('right', 'bottom', '500', '幼圆')
      .opacity(0.6)
      .build();
  }

  init() {
    this.black = this.createRect(layout.PANEL_WIDTH, 1000)
      .fill('#000')
      .z(0, -10)
      .build();
    this.lastX = 100;

    // 画线
    this.lines = columnDesc
      .map(desc => desc.x + layout.ROW_X)
      .map(x => this.createLine(x, 0, x, 10)
        .stroke('#FFF', 4)
        .opacity(0.2)
        .z(0, -1)
        .lineDash([3, 3])
        .build()
    );
  }

  onResize(args) {
    const {height} = args;
    if (!this.hasInited) return;

    this.black.rectShape()
      .height(height)
      .update();

    this.footer.y = height - layout.FOOTER_HEIGHT + layout.TABLE_HEADER_PADDING_TOP_BOTTOM;

    // 连线
    const y1 = layout.TABLE_HEADER_Y + layout.TABLE_HEADER_ICON_Y;
    const y2 = this.footer.y + layout.TABLE_FOOTER_ICON_Y;
    this.lines.forEach(line => line.lineShape().y1(y1).y2(y2).update());
  }

  setBgColor(color) {
    if (!this.hasInited) return;
    this.black.style().fill(color).update();
  }
}

/**
 * 表头
 * - 飞越 / 环绕 / - / 硬着陆 / 软着陆 / 巡视 / - / 着陆后返回
 *
 * 辅助图例: 任务失败 / 部分成功 / 尚未完成
 */
class TableHeader extends ZRenderComp {
  constructor(args) {
		super(args.parent, args.zrender);
    this.useExistZRenderInstance(args.zr, args.x, args.y);
    /** @type {number} */ this.iconStartY = args.iconStartY || 0;
    /** @type {number} */ this.textStartY = args.textStartY || 0;
  }

  init() {
    const { textStartY, iconStartY } = this;
    // font = 华文中宋

    columnDesc.forEach(param => {
      const {x, names, icon} = param;
      const fontSize = layout.TABLE_HEADER_FONT_SIZE;
      const paddingRight = layout.TABLE_HEADER_TEXT_PADDING_RIGHT;

      // 文字是竖排的
      const width = fontSize * names.length + paddingRight * (names.length - 1);
      const baseX = x - width / 2;
      names.forEach((nameText, i) => {
        if (nameText == '') {
          return;
        }

        const xx = baseX + i * (fontSize + paddingRight);
        let deltaY = (layout.TABLE_HEADER_TEXT_HEIGHT - nameText.length * fontSize) /
            (nameText.length - 1) + fontSize;

        for (let ii = 0; ii < nameText.length; ii++) {
          let y = textStartY + ii * deltaY;
          this.createText(nameText[ii], fontSize)
            .textStyle('left', 'top', '500', '华文中宋')
            .textFill('#FFF')
            .position(xx, y)
            .build();
        }
      });

      // 图标
      this.createPathFromString(icon)
        .fill('#FFF')
        .opacity(layout.TABLE_HEADER_ICON_OPACITY)
        .position(x, iconStartY)
        .build();
    });

    // 辅助图例
    const fontSize = 24;
    const textPadding = 30;
    [
      {
        x: 30,
        y: 15,
        text: '任务失败',
        icon: layout.paths.fail,
      },
      {
        x: 220,
        y: 15,
        text: '尚未完成',
        icon: layout.paths.question,
      },
      {
        x: 30,
        y: 65,
        text: '部分成功',
        icon: layout.paths.partially,
      },
    ].forEach(legend => {
      const y = legend.y + textStartY;
      this.createPathFromString(legend.icon)
        .fill('#FFF')
        .position(legend.x, y)
        .build();
      this.createText(legend.text, fontSize)
        .textFill('#FFF')
        .textStyle('left', 'top', '500', '华文中宋')
        .position(legend.x + textPadding, y - fontSize / 2)
        .build();
    });
  }
}

/**
 * 创建的时候, 因为位置 (x, y) 不确定, 所以开始时显示位置错误.
 * 
 * 这个组件要当确定位置后, 更改 x, y 时才能显示.
 */
class Row extends ZRenderComp {
  constructor(args) {
		super(args.parent, args.zrender);
    this.useExistZRenderInstance(args.zr, 0, 0);
    /** @type {MoonRecord} */ const record = this.record = args.record;

    // 行间距
    this.height = layout.ROW_DY;

    // 年份
    this.createText(record.year + '', layout.ROW_YEAR_FONT_SIZE)
      .textFill('#FFF')
      .textStyle('left', 'top', 'bold', 'Arial Black')
      .position(layout.ROW_YEAR_X, layout.ROW_YEAR_Y)
      .build();

    // 图标
    let iconDescs = this.buildDesc();
    iconDescs.forEach(desc => this.drawIcon(desc));

    let minIconX = iconDescs ?
        Math.min(...iconDescs.map(desc => desc.x)) - layout.ROW_PAYLOAD_X :
        layout.iconXs.return_orbit - layout.ROW_PAYLOAD_X;
    let hasMissionSuccess = this.record.isSuccess();

    // 载荷
    // 载荷文本的长度是可变的, 它必须左对齐, 右边的最大边界是右边第一个图标的左边
    // 所以要拿到第一个图标的 x
    this.payloads = new PayloadText({
      parent : this,
      zrender : this.zrender,
      zr : this.zr,
      x : layout.ROW_PAYLOAD_X,
      y : 0,
      text : record.payloads.join(' / '),
      opacity : hasMissionSuccess ? 1 : layout.ROW_PAYLOAD_FAIL_OPACITY,
      maxWidth : minIconX,
    });
    let spPayloadW = this.payloads.spPayloadW;

    // 国家
    let countryDesc = layout.countries.filter(c => c.key == record.country)[0];
    if (countryDesc == null) {
      countryDesc = { key, color:'#AAAAAA', name:key };
    }
    this.tag = new CountryTag({
      parent : this,
      zrender : this.zrender,
      zr : this.zr,
      x : layout.ROW_COUNTRY_X,
      y : layout.ROW_COUNTRY_Y,
      countryDesc,
    });

    // 线条 左
    this.createLine(
        layout.ROW_COUNTRY_X + layout.ROW_COUNTRY_WIDTH,
        layout.ROW_LINE_Y,
        layout.ROW_YEAR_X - layout.ROW_LINE_PADDING_X,
        layout.ROW_LINE_Y)
      .stroke(countryDesc.color, layout.ROW_LINE_WIDTH)
      .z(0, 1)
      .build();

    // 线条 右
    let lastX = spPayloadW + layout.ROW_LINE_PADDING_X + layout.ROW_PAYLOAD_X;
    iconDescs
      .sort((d1, d2) => d1.x - d2.x)
      .forEach(d => {
        this.createLine(lastX, layout.ROW_LINE_Y, d.x, layout.ROW_LINE_Y)
          .stroke(countryDesc.color, layout.ROW_LINE_WIDTH)
          .opacity(d.result == 1 ? 1 : layout.ROW_PAYLOAD_FAIL_OPACITY)
          .z(0, 1)
          .build();
        lastX = d.x;
      });
  }

  /**
   * @param {IconDesc} desc
   */
  drawIcon(desc) {
    const {iconPath, x, result, info} = desc;

    this.createPathFromString(iconPath)
      .fill('#FFF')
      .position(x, 12)
      .z(0, 2)
      .opacity(result <= 0 ? layout.ROW_PAYLOAD_FAIL_OPACITY : 1)
      .build();

    if (info != null) {
      this.createText(info, layout.ROW_ICON_INFO_FONT_SIZE)
        .textFill('#FFF')
        .textStyle('left', 'top', '300', '幼圆')
        .position(x + 15, 12)
        .scale(0.7, 1)
        .z(0, 3)
        .build();
    }

    if (result == 0) { // 表示任务失败
      // 同一个位置画一个叉
      this.createPathFromString(layout.paths.fail)
        .fill('#FFF')
        .position(x, 12)
        .z(0, 3)
        .build();
    } else if (result == -1) { // 表示任务结果未知
      // 同一个位置画一个问号 TODO
    } else if (result == 0.5) { // 表示任务部分成功
      // ICON 右上角加一个较小的 *
      this.createPathFromString(layout.paths.partially)
        .fill('#FFF')
        .position(x + 16, 4)
        .z(0, 3)
        .scale(0.6, 0.6)
        .build();
    }
  }

  buildDesc() {
    const { record } = this;
    let descs = record.missions.map(m => missionToIconDesc(m)).filter(d => d != null);

    if (record.props.manned) {
      let d = new IconDesc();
      d.x = layout.iconXs.manned_land;
      d.iconPath = layout.paths.manned_land;
      d.result = 1;
      d.info = '载人';
      descs.push(d);
    }

    return descs;
  }
}

/**
 * 小结行. 比如: 1990 年代 (1990~1999), 小结 A 国发射任务共 x 次, y 次成功...
 */
class SummaryRow extends ZRenderComp {
  constructor(args) {
    super(args.parent, args.zrender);
    this.useExistZRenderInstance(args.zr, 0, 0);

    /** @type {number} 年代号码 */ this.year = args.year;
    /** @type {object} key:country, value:[总次数, 成功次数] */ this.record = args.record;
    this.showRecord = args.showRecord == null ? true : args.showRecord == true;
  }

  init() {
    let spYear = this.createText(`${this.year}`, layout.SUMMARY_YEAR_FONT_SIZE)
      .textFill('#FF6')
      .position(layout.SUMMARY_YEAR_X, layout.SUMMARY_YEAR_Y)
      .textStyle('left', 'bottom', '500', 'Impact', 'italic')
      .build();
    let spYearW = spYear.getBoundingRect().width;
    this.createText('年代', layout.SUMMARY_YEAR_FONT_SIZE_2)
      .textFill('#FF6')
      .position(layout.SUMMARY_YEAR_X + spYearW + 10, layout.SUMMARY_YEAR_Y)
      .textStyle('left', 'bottom', '500', '方正汉真广标简体', 'italic')
      .build();

    if (!this.showRecord) {
      return;
    }

    /** @type {{country:string, count:number, success:number}[]} */
    let params = [];
    let allCount = 0;
    let allSuccess = 0;
    for (let country in this.record) {
      const [count, success] = this.record[country];
      params.push({ country, count, success });
      allCount += count;
      allSuccess += success;
    }
    params.sort((a, b) => {
      let r = b.count - a.count;
      if (r == 0) {
        r = b.success - a.success;
      }
      return r;
    });

    this.createText('成功次数 / 总任务数', layout.SUMMARY_BAR_FONT_SIZE)
      .textFill('#FFF')
      .position(layout.SUMMARY_BAR_X, layout.SUMMARY_BAR_Y)
      .opacity(layout.SUMMARY_BAR_OPACITY)
      .textStyle('center', 'bottom', '200', '华文中宋')
      .build();
    this.createText(`${allSuccess} / ${allCount}`, layout.SUMMARY_BAR_FONT_SIZE_2)
      .textFill('#FFF')
      .position(layout.SUMMARY_BAR_X, layout.SUMMARY_BAR_Y_2)
      .opacity(layout.SUMMARY_BAR_OPACITY)
      .textStyle('center', 'bottom', '200', 'Impact')
      .build();

    this.countryTags = params.map((p, index) => {
      const { country } = p;

      let countryDesc = layout.countries.filter(c => c.key == country)[0];
      if (countryDesc == null) {
        countryDesc = { key:country, color:'#AAAAAA', name:country };
      }
      let xx = layout.SUMMARY_ITEM_X + index * layout.SUMMARY_ITEM_DX;
      this.createText(`${p.success} / ${p.count}`, layout.SUMMARY_BAR_FONT_SIZE_2)
        .textFill(countryDesc.color)
        .position(xx, layout.SUMMARY_BAR_Y_2)
        .opacity(layout.SUMMARY_BAR_OPACITY)
        .textStyle('center', 'bottom', '200', 'Impact')
        .build();

      return new CountryTag({
        parent : this,
        zrender : this.zrender,
        zr : this.zr,
        x : xx - layout.ROW_COUNTRY_WIDTH / 2,
        y : layout.SUMMARY_ITEM_Y,
        countryDesc,
      });
    });
  }
}

class IconDesc {
  x = 0;
  result = 0;
  /** @type {string} '玉兔 2 号' */
  info = null;
  /** @type {string} */
  iconPath = null;
}

function missionToIconDesc(mission) {
  /** @type {string} */ let key = mission.key;
  /** @type {string} */ let pathName;
  /** @type {string} */ let info;

  switch (key) {
    case 'landing':
      pathName = `${mission.key}_${mission.type == null ? 'hard' : mission.type}`;
      info = (mission.payloads) ? mission.payloads.join('/') : null;
      break;
    default:
      pathName = `${mission.key}${mission.type == null ? '' : '_' + mission.type}`;
      break;
  }

  /** @type {string} */ let path = layout.paths[pathName];
  if (path == null) {
    return null;
  }

  let desc = new IconDesc();
  desc.x = layout.iconXs[pathName];
  desc.info = info;
  desc.result = mission.result;
  desc.iconPath = path;

  return desc;
}

/**
 * 单独处理载荷文本的类.
 * 预想的字体设置: (暂未实现)
 * - 中文: 方正汉真广标简体 | Microsoft YaHei UI
 * - 英文: Impact
 */
class PayloadText extends ZRenderComp {
  constructor(args) {
    super(args.parent, args.zrender);
    this.useExistZRenderInstance(args.zr, args.x, args.y);

    let spPayload = this.createText(args.text, layout.ROW_PAYLOAD_FONT_SIZE)
      .textFill('#FFF')
      .textStyle('left', 'bottom', '500', 'Impact')
      .position(0, 24)
      .opacity(args.opacity)
      .build();

    let spPayloadW = spPayload.getBoundingRect().width;
    let payloadMaxWidth = args.maxWidth - layout.ROW_PAYLOAD_RIGHT_PADDING_DX;
    if (spPayloadW > payloadMaxWidth) {
      spPayload.scaleTo(payloadMaxWidth / spPayloadW, 1);
      spPayloadW = payloadMaxWidth;
    }

    /** @type {number} */ this.spPayloadW = spPayloadW;
  }
}

class CountryTag extends ZRenderComp {
  constructor(args) {
    super(args.parent, args.zrender);
    this.useExistZRenderInstance(args.zr, args.x, args.y);
    /** @type {{key:string,color:string,name:string}} */
    const countryDesc = this.countryDesc = args.countryDesc;

    const width = layout.ROW_COUNTRY_WIDTH;
    const height = layout.ROW_COUNTRY_HEIGHT;

    this.createRect(width, height, 0, 0, [8, 8, 8, 8])
      .stroke(countryDesc.color, 3)
      .fillNone()
      .z(0, 1)
      .build();

    let spTx = this.createText(countryDesc.name, layout.ROW_COUNTRY_FONT_SIZE)
      .textStyle('center', 'center', 'bold', '幼圆')
      .textFill(countryDesc.color)
      .position(width / 2, height / 2)
      .z(0, 2)
      .build();
    let spTxBound = spTx.getBoundingRect();
    if (spTxBound.width > width - 12) {
      spTx.scaleTo((width - 12) / spTxBound.width, 1);
    }
  }
}

export {
	MoonRecordView
};
